/*!
 * A copy of this code can be obtained from:
 * http://code.google.com/p/core-js/
 *
 * The MIT License
 *
 * Copyright (c) 2009 Jyrki Laurila
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
;(function() {
    /* Initializing scope variables, to speed up references */
    var win = this,
    doc = win.document,
    handler = {},
    /**
     * The namespace within window-object where the core will be created
     *
     * @example
     * // in global scope
     * CORE_NS = "myCore";
     *
     * @name CORE_NS
     * @constant
     * @default "core"
     */
    ns = (CORE_NS || "core");
    CORE_NS = ns;
    
    win[ns] = (win[ns] || {});
    
    /**
     * The root namespace for the core-js framework
     *
     * @version $v0.9.4 2009-09-25
     * @namespace The root namespace for the core-js framework
     * @author Jyrki Laurila
     * @name core
     */
    var core = win[ns],
    config = {
        charset: "UTF-8"
    };

    /**
     * Extends an object with another object. If no second parameter is defined, we assume to be extending "this"
     *
     * @example
     * var myProperties = {
     *     foo: "bar",
     *     color: "red",
     *     shape: "circle"
     * };
     *
     * var myObject = {
     *     object: "balloon"
     * };
     *
     * core.extend(myObject, myProperties);
     *
     * alert(myObject.color); // will alert "red"
     *
     * @name core.extend
     * @methodOf core
     * @param {boolean|object} arg If boolean, we assume we want to make a deep copy, otherwise this is the extendee target.
     * @param {arguments} [1..n] The object we extend the target with.
     * @returns {object} Extended object
     */
    function extend() {
        var opt, name, src, copy,
        arg = arguments,
        target = (arg[0] || {}),
        i = 1,
        len = arg.length,
        deep = false;
        
        if (typeof(target) == "boolean") {
            deep = target;
            target = (arg[1] || {});
            i = 2;
        }
        
        if (typeof(target) != "object" && typeof(target) != "function") {
            target = {};
        }
        
        if (len == i) {
            target = this;
            i--;
        }
        
        for (; i < len; i++) {
            if ((opt = arg[i])) {
                for (name in opt) {
                    src = target[name];
                    copy = opt[name];
                    
                    if (target !== copy) {
                        if (deep && copy && typeof(copy) == "object" && !copy.nodeType) {
                            target[name] = extend(deep, (src || (copy.length ? [] : {})), copy);
                        } else if (copy !== undefined) {
                            target[name] = copy;
                        }
                    }
                }
            }
        }
        return target;
    }
    
    /**
     * Generates a namespace from a path and returns it
     *
     * @example
     * var myNameSpace = core.namespace("my.namespace");
     *
     * @example
     * var myObj = {
     *     doStuff: function() {
     *         alert("foo");
     *     }
     * };
     * 
     * core.namespace("my.namespace", myObj);
     * 
     * my.namespace.doStuff();
     *
     * @name core.namespace
     * @methodOf core
     * @param {string} path A dot separated path of the namespace
     * @param {mixed} [obj] The value that will be set to the target property described in path. Can be anything.
     * @param {boolean} [ext] If true, the end object will be an extendee for the target object. Defaults to false, which replaces the target object.
     * @returns {object} The generated namespace
     */
    function namespace(path, obj, ext) {
        var cur,
        root = this,
        src = path.split("."),
        len = src.length,
        i = 0;
        
        obj = (obj || {});
        for (; i < len; i++) {
            cur = (root[src[i]] || {});
            if (i == (len - 1)) {
                cur = (ext ? extend(cur, obj) : obj);
            }
            root = root[src[i]] = cur;
        }

        return root;
    }
    
    /**
     * Includes an external script or style from a specific location based on the name and/or the config path
     *
     * @example
     * core.include("/scripts/myscript.js");
     *
     * @example
     * core.include("/path/to/script.js", {
     *     callback: function(src) {
     *         alert("Script '" + src + "' loaded successfully");
     *     }
     * });
     * 
     * @example
     * core.include("http://my.url.com/path/to/script.js");
     *
     * @name core.include
     * @methodOf core
     * @param {string} src
     * @param {object} [cfg] Configuration object
     * @param {string} [cfg.type] Type of the file to be loaded. "style" or "script" (default)
     * @param {string} [cfg.charset] Charset of the script to be included. Defaults to "UTF-8"
     * @param {function} [cfg.callback] The function to call after the import is done (works with type "script")
     * @param {object} [cfg.scope] Scope of the callback (the "this" object). Default is null (=global object).
     */
    function include(src, cfg) {
        var el,
        path = src,
        c = extend({}, {
            type: "script",
            scope: null
        }, config, cfg),
        ready = false,
        fn = handler[src.split(".")[0]],
        head = doc.getElementsByTagName("head")[0];
        
        if (typeof(fn) == "function" && !(path = fn(src, cfg))) {
            return false;
        }
        
        c.src = path;
        
        if (c.type == "style") {
            el = doc.createElement("link");
            el.rel = "stylesheet";
            el.type = "text/css";
            el.href = path;
        } else {
            el = doc.createElement("script");
            el.type = "text/javascript";
            el.charset = c.charset;
            el.src = path;
            if (typeof(c.callback) == "function") {
                el.onload = el.onreadystatechange = function() {
                    if (!ready && (!this.readyState || this.readyState == 'complete' || this.readyState == 'loaded')) {
                        ready = true;
                        el.onload = el.onreadystatechange = null;
                        head.removeChild(el);
                        c.callback.call((c.scope || this), src, c);
                    }
                };
            }
        }
        
        head.insertBefore(el, head.firstChild);
    }
    
    /**
     * Sets an include handler for a specific namespace.
     * 
     * @example
     * core.setIncludeHandler("my", function(src) {
     *     return "http://my.url.com/scripts/" + src.split(".").join("/") + ".js";
     * });
     * 
     * core.include("my.module.action"); // Will load http://my.url.com/scripts/my/module/action.js
     * 
     * @name core.setIncludeHandler
     * @methodOf core
     * @param {string} root The root namespace that we want to handle
     * @param {function} fn The function that handles the include or the src.
     * If this function returns false, the include process will be halted.
     */
    function setIncludeHandler(root, fn) {
        if (root && fn) {
            handler[root] = fn;
        }
    }
    
    extend(core, {
        extend: extend,
        namespace: namespace,
        include: include,
        setIncludeHandler: setIncludeHandler,
        config: config
    });

})();
